<!DOCTYPE html>
<html>

<head>
    <title>Customize your own components </title>
    <script type="text/javascript" src="https://unpkg.com/dat.gui@0.7.6/build/dat.gui.min.js"></script>
    <link type="text/css" rel="stylesheet" href="https://unpkg.com/maptalks/dist/maptalks.css">
    <script type="text/javascript" src="https://unpkg.com/maptalks/dist/maptalks.js"></script>
    <script type="text/javascript" src="https://unpkg.com/@maptalks/gl/dist/maptalksgl.js"></script>
    <script type="text/javascript" src="https://unpkg.com/three@0.138.0/build/three.min.js"></script>
    <script type="text/javascript"
        src="https://unpkg.com/maptalks.three@latest/dist/maptalks.three.js"></script>
    <style>
        html,
        body {
            margin: 0px;
            height: 100%;
            width: 100%;
        }

        #map {
            width: 100%;
            height: 100%;
            background-color: #000;
        }
    </style>
</head>

<body>
    <div id="map"></div>
    <script>

        var map = new maptalks.Map("map", {
            center: [13.416935229170008, 52.529564137540376],
            zoom: 2,
            pitch: 0,
            bearing: 0,

            centerCross: true,
            doubleClickZoom: false,
            // baseLayer: new maptalks.TileLayer('tile', {
            //     urlTemplate: 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png',
            //     subdomains: ['a', 'b', 'c', 'd'],
            //     attribution: '&copy; <a href="http://osm.org">OpenStreetMap</a> contributors, &copy; <a href="https://carto.com/">CARTO</a>'
            // })
        });


        var threeLayer = new maptalks.ThreeLayer('t', {
            forceRenderOnMoving: true,
            forceRenderOnRotating: true,
            // animation: true
        });


        threeLayer.prepareToDraw = function (gl, scene, camera) {
            var light = new THREE.DirectionalLight(0xffffff);
            light.position.set(0, -10, 10).normalize();
            scene.add(light);
            addCircles();

        };
        const sceneConfig = {
            postProcess: {
                enable: true,
                antialias: { enable: true }
            }
        };
        const groupLayer = new maptalks.GroupGLLayer('group', [threeLayer], { sceneConfig });
        groupLayer.addTo(map);

        var material = new THREE.MeshBasicMaterial({ transparent: true });
        var circles, firstCircle;
        function addCircles() {
            // data from https://antv-l7.gitee.io/zh/examples/heatmap/grid#grid_world
            fetch('https://gw.alipayobjects.com/os/bmw-prod/3dadb1f5-8f54-4449-8206-72db6e142c40.json').then(res => res.json()).then(geojson => {
                const lnglats = geojson.features.map(f => {
                    return f.geometry.coordinates;
                });
                const radius = 50000;
                circles = lnglats.map(function (lnglat) {
                    var circle;
                    if (!firstCircle) {
                        circle = firstCircle = new Circle(lnglat, {
                            radius: radius,
                            interactive: false
                        }, material, threeLayer);
                    } else {
                        circle = getCircle(lnglat, { radius: radius });
                    }
                    return circle;
                });
                threeLayer.addMesh(circles);
                animation();
                initGui();
            })
        }

        function animation() {
            // layer animation support Skipping frames
            threeLayer._needsUpdate = !threeLayer._needsUpdate;
            if (threeLayer._needsUpdate && !threeLayer.isRendering()) {
                threeLayer.redraw();
            }
            requestAnimationFrame(animation);
        }


        function initGui() {
            var params = {
                add: true,
                color: material.color.getStyle(),
                show: true,
                opacity: 1,
                altitude: 0,
            };

            var gui = new dat.GUI();
            gui.add(params, 'add').onChange(function () {
                if (params.add) {
                    threeLayer.addMesh(circles);
                } else {
                    threeLayer.removeMesh(circles);
                }
            });
            gui.addColor(params, 'color').name('circle color').onChange(function () {

                material.color.setStyle(params.color);
            });
            gui.add(params, 'opacity', 0, 1).onChange(function () {
                material.opacity = params.opacity;
            });
            gui.add(params, 'show').onChange(function () {
                circles.forEach(function (mesh) {
                    if (params.show) {
                        mesh.show();
                    } else {
                        mesh.hide();
                    }
                });
            });
        }


        function getCircle(coordinate, options) {
            const { radius } = firstCircle.getOptions();
            const geometry = firstCircle.getObject3d().geometry;
            const scaleR = options.radius / radius;
            const circle = new maptalks.BaseObject();
            circle._initOptions(Object.assign({}, firstCircle.getOptions(), options));
            circle._createMesh(geometry, material);
            circle.getObject3d().scale.set(scaleR, scaleR, 1);
            const { altitude } = options;
            const layer = circle.getLayer();
            const z = layer.altitudeToVector3(altitude, altitude).x;
            const position = layer.coordinateToVector3(coordinate, z);
            circle.getObject3d().position.copy(position);
            return circle;
        }



        //default values
        var OPTIONS = {
            radius: 100,
            altitude: 0
        };

        /**
         * custom Circle component
         * 
         * you can customize your own components
         * */

        class Circle extends maptalks.BaseObject {
            constructor(coordinate, options, material, layer) {
                options = maptalks.Util.extend({}, OPTIONS, options, { layer, coordinate });
                super();
                //Initialize internal configuration
                // https://github.com/maptalks/maptalks.three/blob/1e45f5238f500225ada1deb09b8bab18c1b52cf2/src/BaseObject.js#L135
                this._initOptions(options);
                const { altitude, radius } = options;
                //generate geometry
                const r = layer.distanceToVector3(radius, radius).x
                const geometry = new THREE.CircleBufferGeometry(r, 50);

                //Initialize internal object3d
                // https://github.com/maptalks/maptalks.three/blob/1e45f5238f500225ada1deb09b8bab18c1b52cf2/src/BaseObject.js#L140
                this._createMesh(geometry, material);

                //set object3d position
                const z = layer.altitudeToVector3(altitude, altitude).x;
                const position = layer.coordinateToVector3(coordinate, z);
                this.getObject3d().position.copy(position);
                // this.getObject3d().rotation.x = -Math.PI;
            }
        }






    </script>
</body>

</html>